package sharewithus.content.dao.cacheimpl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

import net.rubyeye.xmemcached.GetsResponse;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.transcoders.CachedData;
import net.rubyeye.xmemcached.transcoders.PrimitiveTypeTranscoder;
import net.rubyeye.xmemcached.transcoders.Transcoder;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import sharewithus.content.dao.IFeedCache;
import sharewithus.content.dao.cacheimpl.encoder.BeanEncoder;
import sharewithus.content.dto.Feed;

public class FeedCacheImpl implements IFeedCache {

	private static final String JEDIS_CHARSET = "UTF-8";

	private static final String REDIS_KEY_ONWER_PREX = "feed-owner:";

	private static int SPECIAL_BYTEARRAY = (8 << 8);

	private static final int EXPIRE_TIME = 60 * 60 * 24 * 30;

	private Transcoder<Feed> transcoder = new FeedTranscoder();

	@Autowired
	private MemcachedClient memcachedClient;

	@Autowired
	@Qualifier("shardedJedisPool")
	private ShardedJedisPool jedisMgr;

	@Autowired
	private BeanEncoder<Feed> feedEncoder;

	public void saveFeed(Feed feed) {

		try {
			memcachedClient
					.add(feed.getFeedId(), EXPIRE_TIME, feed, transcoder);
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MemcachedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public Feed getFeedById(String feedId) {
		Feed cachedFeed = null;
		try {
			cachedFeed = memcachedClient.get(feedId, transcoder);
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MemcachedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return cachedFeed;
	}

	public Map<String, GetsResponse<Feed>> getFeedbyIds(List<String> feedIds) {
		Map<String, GetsResponse<Feed>> cachedFeeds = new HashMap<String, GetsResponse<Feed>>();
		try {
			cachedFeeds = memcachedClient.gets(feedIds, transcoder);
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MemcachedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return cachedFeeds;
	}

	public void pushOwnerFeedRelationship(Feed feed) {
		// TODO Auto-generated method stub
		String feedListKey = REDIS_KEY_ONWER_PREX + feed.getOwnerId();
		String feedJson = feedEncoder.getFrJson(feed);
		ShardedJedis jedis = null;
		try {
			jedis = jedisMgr.getResource();
			jedis.lpush(feedListKey, feedJson);
		} finally {
			if (jedis != null)
				jedisMgr.returnResource(jedis);
		}
	}

	public List<Feed> getOwnerFeeds(String ownerId) {
		String feedListKey = REDIS_KEY_ONWER_PREX + ownerId;
		ShardedJedis jedis = null;
		List<String> feedJsons = new LinkedList<String>();
		try {
			jedis = jedisMgr.getResource();
			feedJsons = jedis.lrange(feedListKey, 0, -1);
		} finally {
			if (jedis != null)
				jedisMgr.returnResource(jedis);
		}
		Iterator<String> iter = feedJsons.iterator();

		List<Feed> feeds = new LinkedList<Feed>();

		String feedJson;
		while (iter.hasNext()) {
			feedJson = iter.next();
			feeds.add(feedEncoder.parseJson(feedJson));
		}
		return feeds;
	}

	public List<Feed> getOwnerFeedPage(String ownerId, int pageSize,
			int pageNumber) {
		String feedListKey = REDIS_KEY_ONWER_PREX + ownerId;
		ShardedJedis jedis = null;
		List<String> feedJsons = new LinkedList<String>();
		try {
			jedis = jedisMgr.getResource();
			long savedSize = jedis.llen(feedListKey);

			long startNumber = pageSize * (pageNumber - 1);
			long endNumber = pageSize * pageNumber - 1;

			if (savedSize > endNumber) {
				feedJsons = jedis.lrange(feedListKey, startNumber, endNumber);
			}
			if (startNumber <= savedSize && savedSize <= endNumber) {
				feedJsons = jedis.lrange(feedListKey, startNumber, -1);
			} else {
				// feedJsons = jedis.lrange(feedListKey, 0, -1);
			}
		} finally {
			if (jedis != null)
				jedisMgr.returnResource(jedis);
		}
		Iterator<String> iter = feedJsons.iterator();

		List<Feed> feeds = new LinkedList<Feed>();

		String feedJson;
		while (iter.hasNext()) {
			feedJson = iter.next();
			feeds.add(feedEncoder.parseJson(feedJson));
		}
		return feeds;
	}

	/**
	 * Transcoder for Feed
	 * 
	 * @author Bruce Wang
	 * 
	 */
	private class FeedTranscoder extends PrimitiveTypeTranscoder<Feed>
			implements Transcoder<Feed> {
		ObjectMapper mapper = new ObjectMapper();

		public CachedData encode(Feed feed) {
			byte[] b = null;
			try {
				b = mapper.writeValueAsBytes(feed);
			} catch (JsonGenerationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JsonMappingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			return new CachedData(SPECIAL_BYTEARRAY, b);
		}

		public Feed decode(CachedData d) {
			if (SPECIAL_BYTEARRAY == d.getFlag()) {
				Feed obj = null;
				try {
					obj = mapper.readValue(d.getData(), Feed.class);
				} catch (JsonParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (JsonMappingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return obj;
			} else {
				return null;
			}
		}
	}

}
